package com.juc.concurrency.jucExample.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 信号量 做并发控制 如果为1 那么就是单线程了
 * 本例子中是30个线程全部放到线程池 信号量是3 效果是每3秒执行3个
 * 常用API:
 * acquire() 获取一个许可。step into源码，不加参数的情况默认获取1个许可
 * acquire(int permits) 获取指定个数的许可
 * tryAcquire()方法尝试获取1个许可证
 * tryAcquire(long timeout, TimeUnit unit) 最大等待许可的时间
 * tryAcquire(int permits) 获取指定个数的许可
 * tryAcquire(int permits, long timeout, TimeUnit unit) 最大等待许可的时间
 * availablePermits() ： 返回此信号量中当前可用的许可证数
 * release() 释放许可。。step into源码，不加参数的情况默认释放1个许可
 * release(int permits) 释放指定个数的许可
 * int getQueueLength() 返回正在等待获取许可证的线程数。
 * boolean hasQueuedThreads() 是否有线程正在等待获取许可证。
 * void reducePermits(int reduction) 减少reduction个许可证。是个protected方法。
 * Collection getQueuedThreads() 返回所有等待获取许可证的线程集合。是个protected方法。
 * 【Semaphore构造为0的解释】
 * Semaphore是用来保护一个或者多个共享资源的访问，Semaphore内部维护了一个计数器，其值为可以访问的共享资源的个数。
 * 一个线程要访问共享资源，先获得信号量，如果信号量的计数器值大于1，意味着有共享资源可以访问，则使其计数器值减去1，再访问共享资源。
 * 如果计数器值为0,线程进入休眠。当某个线程使用完共享资源后，释放信号量，并将信号量内部的计数器加1，之前进入休眠的线程将被唤醒并再次试图获得信号量
 */
@Slf4j
public class SemaphoreExample1 {

    private final static int threadCount = 30;

    public static void main(String[] args) throws Exception {

        ExecutorService exec = Executors.newCachedThreadPool();

        //改成1 效果更明显
        final Semaphore semaphore = new Semaphore(3);
        //第二个构造方法指定是公平模式还是非公平模式,默认非公平模式 . 公平模式：先启动的线程优先得到许可。 非公平模式：先启动的线程并不一定先获得许可，谁抢到谁就获得许可
        //final Semaphore semaphore = new Semaphore(3, true);

        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    semaphore.acquire(); // 获取一个许可
                    test(threadNum);
                    semaphore.release(); // 释放一个许可
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        exec.shutdown();
    }

    private static void test(int threadNum) throws Exception {
        log.info("{}", threadNum);
        Thread.sleep(2000);
    }
}
